home *** CD-ROM | disk | FTP | other *** search
/ Amiga Games Extra 1996 September / Amiga Games Extra CD-ROM 9-1996.iso / userbox / publicdomain / vim-4.2 / src / gui.c < prev    next >
C/C++ Source or Header  |  1996-06-09  |  46KB  |  2,024 lines

  1. /* vi:set ts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved            by Bram Moolenaar
  4.  *                                GUI/Motif support by Robert Webb
  5.  *
  6.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  7.  * Do ":help credits" in Vim to see a list of people who contributed.
  8.  */
  9.  
  10. #include "vim.h"
  11. #include "globals.h"
  12. #include "proto.h"
  13. #include "option.h"
  14.  
  15. /* Structure containing all the GUI information */
  16. Gui gui;
  17.  
  18. /* Set to TRUE after adding/removing menus to ensure they are updated */
  19. int force_menu_update = FALSE;
  20.  
  21.  
  22. static void gui_check_screen __ARGS((void));
  23. static void gui_outstr __ARGS((char_u *, int));
  24. static void gui_update_selection __ARGS((void));
  25. static int gui_get_menu_cmd_modes __ARGS((char_u *, int, int *, int *));
  26. static int gui_add_menu_path __ARGS((char_u *, int, void (*)(), char_u *, int));
  27. static int gui_remove_menu __ARGS((GuiMenu **, char_u *, int));
  28. static void gui_free_menu __ARGS((GuiMenu *));
  29. static void gui_free_menu_string __ARGS((GuiMenu *, int));
  30. static int gui_show_menus __ARGS((char_u *, int));
  31. static void gui_show_menus_recursive __ARGS((GuiMenu *, int, int));
  32. static char_u *gui_menu_name_skip __ARGS((char_u *name));
  33. static void gui_create_initial_menus __ARGS((GuiMenu *, GuiMenu *));
  34. static void gui_update_scrollbars __ARGS((void));
  35. static void gui_update_horiz_scrollbar __ARGS((void));
  36.  
  37. /*
  38.  * The Athena scrollbars can move the thumb to after the end of the scrollbar,
  39.  * this makes the thumb indicate the part of the text that is shown.  Motif
  40.  * can't do this.
  41.  */
  42. #ifdef USE_GUI_ATHENA
  43. # define SCROLL_PAST_END
  44. #endif
  45.  
  46. /*
  47.  * gui_start -- Called when user wants to start the GUI.
  48.  */
  49.     void
  50. gui_start()
  51. {
  52.     char_u    *old_term;
  53.  
  54.     old_term = strsave(term_strings[KS_NAME]);
  55.     mch_setmouse(FALSE);                    /* first switch mouse off */
  56.  
  57.     /* set_termname() will call gui_init() to start the GUI */
  58.     termcapinit((char_u *)"builtin_gui");
  59.  
  60.     if (!gui.in_use)                        /* failed to start GUI */
  61.         termcapinit(old_term);
  62.  
  63.     vim_free(old_term);
  64.  
  65.     /*
  66.      * Quit the current process and continue in the child.
  67.      * Makes "gvim file" disconnect from the shell it was started in.
  68.      * Don't do this when Vim was started with "-f" or the 'f' flag is present
  69.      * in 'guioptions'.
  70.      */
  71.     if (gui.in_use && gui.dofork &&
  72.                       vim_strchr(p_guioptions, GO_FORG) == NULL && fork() > 0)
  73.         exit(0);
  74. }
  75.  
  76. /*
  77.  * Call this when vim starts up, whether or not the GUI is started
  78.  */
  79.     void
  80. gui_prepare(argc, argv)
  81.     int        *argc;
  82.     char    **argv;
  83. {
  84.     /* Menu items may be added before the GUI is started, so set this now */
  85.     gui.root_menu = NULL;
  86.     gui.in_use = FALSE;                /* No GUI yet (maybe later) */
  87.     gui.starting = FALSE;            /* No GUI yet (maybe later) */
  88.     gui.dofork = TRUE;                /* default is to use fork() */
  89.     gui_mch_prepare(argc, argv);
  90. }
  91.  
  92. static struct default_menu
  93. {
  94.     char    *name;            /* name of menu item */
  95.     int        mode;            /* mode where menu is valid */
  96.     char    *command;        /* resulting command */
  97. } default_menus[] = 
  98. {
  99.     /* Help menu.  Some reason Motif is happier if this is added first. */
  100.     {"Help.Overview  <F1>",    MENU_NORMAL_MODE,    ":help\r"},
  101.     {"Help.Overview  <F1>",    MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
  102.                                                 "\033:help\r"},
  103.     {"Help.How to\\.\\.\\.",MENU_NORMAL_MODE,    ":help how_to\r"},
  104.     {"Help.How to\\.\\.\\.",MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
  105.                                                 "\033:help how_to\r"},
  106.     {"Help.GUI",            MENU_NORMAL_MODE,    ":help gui\r"},
  107.     {"Help.GUI",            MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
  108.                                                 "\033:help gui\r"},
  109.     {"Help.Version",        MENU_NORMAL_MODE,    ":version\r"},
  110.     {"Help.Version",        MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
  111.                                                 "\033:version\r"},
  112.     {"Help.Credits",        MENU_NORMAL_MODE,    ":help credits\r"},
  113.     {"Help.Credits",        MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
  114.                                                 "\033:help credits\r"},
  115.     {"Help.Copying",        MENU_NORMAL_MODE,    ":help uganda\r"},
  116.     {"Help.Copying",        MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
  117.                                                 "\033:help uganda\r"},
  118.  
  119.     /* File menu */
  120.     {"File.Save       :w",    MENU_NORMAL_MODE,    ":w\r"},
  121.     {"File.Save       :w",    MENU_INSERT_MODE,    "\017:w\r"},
  122.  
  123.     {"File.Close      :q",    MENU_NORMAL_MODE,    ":q\r"},
  124.     {"File.Close      :q",    MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
  125.                                                 "\033:q\r"},
  126.  
  127.     {"File.Quit       :qa",    MENU_NORMAL_MODE,    ":qa\r"},
  128.     {"File.Quit       :qa",    MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
  129.                                                 "\033:qa\r"},
  130.  
  131.     {"File.Save-Quit  :wqa",MENU_NORMAL_MODE,    ":wqa\r"},
  132.     {"File.Save-Quit  :wqa",MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
  133.                                                 "\033:wqa\r"},
  134.  
  135.     /* Edit menu */
  136.     {"Edit.Undo",            MENU_NORMAL_MODE,    "u"},
  137.     {"Edit.Redo",            MENU_NORMAL_MODE,    "\022"},
  138.  
  139.     {"Edit.Cut",            MENU_VISUAL_MODE,    "x"},
  140.     {"Edit.Copy",            MENU_VISUAL_MODE,    "y"},
  141.     {"Edit.Put Before",        MENU_NORMAL_MODE,    "[p"},
  142.     {"Edit.Put Before",        MENU_INSERT_MODE,    "\017[p"},
  143.     {"Edit.Put After",        MENU_NORMAL_MODE,    "]p"},
  144.     {"Edit.Put After",        MENU_INSERT_MODE,    "\017]p"},
  145.     {"Edit.Paste",            MENU_NORMAL_MODE,    "i\022*\033"},    /* CTRL-R * */
  146.     {"Edit.Paste",            MENU_INSERT_MODE|MENU_CMDLINE_MODE,
  147.                                                 "\022*"},    /* CTRL-R * */
  148.     {NULL,                    0,                    NULL}
  149. };
  150.  
  151. /*
  152.  * This is the call which starts the GUI.
  153.  */
  154.     void
  155. gui_init()
  156. {
  157.     char_u    *env_str;
  158.     int        i;
  159.  
  160.     gui.dying = FALSE;
  161.     gui.in_focus = FALSE;
  162.     gui.dragged_sb = SB_NONE;
  163.     gui.dragged_wp = NULL;
  164.     gui.col = gui.num_cols = 0;
  165.     gui.row = gui.num_rows = 0;
  166.  
  167.     /* Initialise gui.cursor_row: */
  168.     INVALIDATE_CURSOR();
  169.     gui.scroll_region_top = 0;
  170.     gui.scroll_region_bot = Rows - 1;
  171.     gui.highlight_mask = HL_NORMAL;
  172.     gui.char_width = 0;
  173.     gui.char_height = 0;
  174.     gui.char_ascent = 0;
  175.     gui.border_width = 0;
  176.  
  177.     gui.selection.owned = FALSE;
  178.     gui.selection.start.lnum = 0;
  179.     gui.selection.start.col = 0;
  180.     gui.selection.end.lnum = 0;
  181.     gui.selection.end.col = 0;
  182.     gui.selection.state = SELECT_CLEARED;
  183.  
  184.     gui.root_menu = NULL;
  185.     gui.menu_is_active = TRUE;        /* default: include menu */
  186.  
  187.     gui.scrollbar_width = SB_DEFAULT_WIDTH;
  188.     gui.menu_height = MENU_DEFAULT_HEIGHT;
  189.     for (i = 0; i < 3; i++)
  190.         gui.new_sb[i] = FALSE;
  191.  
  192.     gui.prev_wrap = -1;
  193.  
  194.     for (i = 0; default_menus[i].name != NULL; ++i)
  195.         gui_add_menu_path((char_u *)default_menus[i].name,
  196.                                     default_menus[i].mode, gui_menu_cb,
  197.                           (char_u *)default_menus[i].command, TRUE);
  198.  
  199.     /*
  200.      * Switch on the mouse by default.
  201.      * This can be changed in the .gvimrc.
  202.      */
  203.     set_string_option((char_u *)"mouse", -1, (char_u *)"a", TRUE);
  204.  
  205.     /*
  206.      * Get system wide defaults for gvim (Unix only)
  207.      */
  208. #ifdef HAVE_CONFIG_H
  209.     do_source(sys_gvimrc_fname, FALSE);
  210. #endif
  211.  
  212.     /*
  213.      * Try to read GUI initialization commands from the following places:
  214.      * - environment variable GVIMINIT
  215.      * - the user gvimrc file (~/.gvimrc for Unix)
  216.      * The first that exists is used, the rest is ignored.
  217.      */
  218.     if ((env_str = vim_getenv((char_u *)"GVIMINIT")) != NULL && *env_str != NUL)
  219.     {
  220.         sourcing_name = (char_u *)"GVIMINIT";
  221.         do_cmdline(env_str, TRUE, TRUE);
  222.         sourcing_name = NULL;
  223.     }
  224.     else
  225.         do_source((char_u *)USR_GVIMRC_FILE, FALSE);
  226.  
  227.     /*
  228.      * Read initialization commands from ".gvimrc" in current directory.  This
  229.      * is only done if the 'exrc' option is set.  Because of security reasons
  230.      * we disallow shell and write commands now, except for unix if the file is
  231.      * owned by the user or 'secure' option has been reset in environment of
  232.      * global ".gvimrc".  Only do this if GVIMRC_FILE is not the same as
  233.      * USR_GVIMRC_FILE or sys_gvimrc_fname.
  234.      */
  235.     if (p_exrc)
  236.     {
  237. #ifdef UNIX
  238.         {
  239.             struct stat s;
  240.  
  241.             /* if ".gvimrc" file is not owned by user, set 'secure' mode */
  242.             if (stat(GVIMRC_FILE, &s) || s.st_uid != getuid())
  243.                 secure = p_secure;
  244.         }
  245. #else
  246.         secure = p_secure;
  247. #endif
  248.  
  249.         i = FAIL;
  250.         if (fullpathcmp((char_u *)USR_GVIMRC_FILE,
  251.                                             (char_u *)GVIMRC_FILE) != FPC_SAME
  252. #ifdef HAVE_CONFIG_H
  253.                 && fullpathcmp(sys_gvimrc_fname,
  254.                                             (char_u *)GVIMRC_FILE) != FPC_SAME
  255. #endif
  256.                 )
  257.             i = do_source((char_u *)GVIMRC_FILE, FALSE);
  258.     }
  259.  
  260.     /*
  261.      * Actually start the GUI itself.
  262.      */
  263.     gui.in_use = TRUE;        /* Must be set after menus have been set up */
  264.     if (gui_mch_init() == FAIL)
  265.     {
  266.         gui.in_use = FALSE;
  267.         return;
  268.     }
  269.  
  270.     maketitle();
  271.  
  272.     gui_create_initial_menus(gui.root_menu, NULL);
  273. }
  274.  
  275.     void
  276. gui_exit()
  277. {
  278.     gui.in_use = FALSE;
  279.     gui_mch_exit();
  280. }
  281.  
  282. /*
  283.  * Set the font. Uses the 'font' option. The first font name that works is
  284.  * used. If none is found, use the default font.
  285.  */
  286.     int
  287. gui_init_font()
  288. {
  289. #define FONTLEN 100
  290.     char_u    *font_list;
  291.     char_u    font_name[FONTLEN];
  292.  
  293.     if (!gui.in_use)
  294.         return FAIL;
  295.  
  296.     for (font_list = p_guifont; *font_list != NUL; )
  297.     {
  298.         /* Isolate one font name */
  299.         (void)copy_option_part(&font_list, font_name, FONTLEN, ",");
  300.         if (gui_mch_init_font(font_name) == OK)
  301.             return OK;
  302.     }
  303.  
  304.     /*
  305.      * Couldn't load any font in 'font', tell gui_mch_init_font() to try and
  306.      * find a font we can load.
  307.      */
  308.       return gui_mch_init_font(NULL);
  309. }
  310.  
  311.     void
  312. gui_set_cursor(row, col)
  313.     int        row;
  314.     int        col;
  315. {
  316.     gui.row = row;
  317.     gui.col = col;
  318. }
  319.  
  320. /*
  321.  * gui_check_screen - check if the cursor is on the screen.
  322.  */
  323.     static void
  324. gui_check_screen()
  325. {
  326.     if (gui.row >= Rows)
  327.         gui.row = Rows - 1;
  328.     if (gui.col >= Columns)
  329.         gui.col = Columns - 1;
  330.     if (gui.cursor_row >= Rows || gui.cursor_col >= Columns)
  331.         INVALIDATE_CURSOR();
  332. }
  333.  
  334.     void
  335. gui_update_cursor()
  336. {
  337.     gui_check_screen();
  338.     if (gui.row != gui.cursor_row || gui.col != gui.cursor_col)
  339.     {
  340.         gui_undraw_cursor();
  341.         gui.cursor_row = gui.row;
  342.         gui.cursor_col = gui.col;
  343.         gui_mch_draw_cursor();
  344.     }
  345. }
  346.  
  347. /*
  348.  * Should be called after the GUI window has been resized.  Its arguments are
  349.  * the new width and height of the window in pixels.
  350.  */
  351.     void
  352. gui_resize_window(pixel_width, pixel_height)
  353.     int        pixel_width;
  354.     int        pixel_height;
  355. {
  356.     gui.num_cols = (pixel_width - 2 * gui.border_offset) / gui.char_width;
  357.     gui.num_rows = (pixel_height - 2 * gui.border_offset) / gui.char_height;
  358.  
  359.     gui_reset_scroll_region();
  360.     /*
  361.      * At the "more" prompt there is no redraw, put the cursor at the last
  362.      * line here (why does it have to be one row too low???).
  363.      */
  364.     if (State == ASKMORE)
  365.         gui.row = gui.num_rows;
  366.  
  367.     if (gui.num_rows != screen_Rows || gui.num_cols != screen_Columns)
  368.         set_winsize(0, 0, FALSE);
  369.     gui_update_cursor();
  370. }
  371.  
  372. /*
  373.  * Make scroll region cover whole screen.
  374.  */
  375.     void
  376. gui_reset_scroll_region()
  377. {
  378.     gui.scroll_region_top = 0;
  379.     gui.scroll_region_bot = gui.num_rows - 1;
  380. }
  381.  
  382.     void
  383. gui_start_highlight(mask)
  384.     long_u    mask;
  385. {
  386.     gui.highlight_mask |= mask;
  387. }
  388.  
  389.     void
  390. gui_stop_highlight(mask)
  391.     long_u    mask;
  392. {
  393.     gui.highlight_mask &= ~mask;
  394. }
  395.  
  396.     void
  397. gui_write(s, len)
  398.     char_u    *s;
  399.     int        len;
  400. {
  401.     char_u    *p;
  402.     int        arg1 = 0, arg2 = 0;
  403.  
  404. /* #define DEBUG_GUI_WRITE */
  405. #ifdef DEBUG_GUI_WRITE
  406.     {
  407.         int i;
  408.         char_u *str;
  409.  
  410.         printf("gui_write(%d):\n    ", len);
  411.         for (i = 0; i < len; i++)
  412.             if (s[i] == ESC)
  413.             {
  414.                 if (i != 0)
  415.                     printf("\n    ");
  416.                 printf("<ESC>");
  417.             }
  418.             else
  419.             {
  420.                 str = transchar(s[i]);
  421.                 if (str[0] && str[1])
  422.                     printf("<%s>", (char *)str);
  423.                 else
  424.                     printf("%s", (char *)str);
  425.             }
  426.         printf("\n");
  427.     }
  428. #endif
  429.     while (len)
  430.     {
  431.         if (s[0] == '\n')
  432.         {
  433.             len--;
  434.             s++;
  435.             gui.col = 0;
  436.             if (gui.row < gui.scroll_region_bot)
  437.                 gui.row++;
  438.             else
  439.                 gui_mch_delete_lines(gui.scroll_region_top, 1);
  440.         }
  441.         else if (s[0] == '\r')
  442.         {
  443.             len--;
  444.             s++;
  445.             gui.col = 0;
  446.         }
  447.         else if (s[0] == Ctrl('G'))        /* Beep */
  448.         {
  449.             gui_mch_beep();
  450.             len--;
  451.             s++;
  452.         }
  453.         else if (s[0] == ESC && s[1] == '|')
  454.         {
  455.             p = s + 2;
  456.             if (isdigit(*p))
  457.             {
  458.                 arg1 = getdigits(&p);
  459.                 if (p > s + len)
  460.                     break;
  461.                 if (*p == ';')
  462.                 {
  463.                     ++p;
  464.                     arg2 = getdigits(&p);
  465.                     if (p > s + len)
  466.                         break;
  467.                 }
  468.             }
  469.             switch (*p)
  470.             {
  471.                 case 'C':        /* Clear screen */
  472.                     gui_mch_clear_block(0, 0, Rows - 1, Columns - 1);
  473.                     break;
  474.                 case 'M':        /* Move cursor */
  475.                     gui_set_cursor(arg1, arg2);
  476.                     break;
  477.                 case 'R':        /* Set scroll region */
  478.                     if (arg1 < arg2)
  479.                     {
  480.                         gui.scroll_region_top = arg1;
  481.                         gui.scroll_region_bot = arg2;
  482.                     }
  483.                     else
  484.                     {
  485.                         gui.scroll_region_top = arg2;
  486.                         gui.scroll_region_bot = arg1;
  487.                     }
  488.                     break;
  489.                 case 'd':        /* Delete line */
  490.                     gui_mch_delete_lines(gui.row, 1);
  491.                     break;
  492.                 case 'D':        /* Delete lines */
  493.                     gui_mch_delete_lines(gui.row, arg1);
  494.                     break;
  495.                 case 'i':        /* Insert line */
  496.                     gui_mch_insert_lines(gui.row, 1);
  497.                     break;
  498.                 case 'I':        /* Insert lines */
  499.                     gui_mch_insert_lines(gui.row, arg1);
  500.                     break;
  501.                 case '$':        /* Clear to end-of-line */
  502.                     gui_mch_clear_block(gui.row, gui.col, gui.row, Columns - 1);
  503.                     break;
  504.                 case 'h':        /* Turn on highlighting */
  505.                     gui_start_highlight(arg1);
  506.                     break;
  507.                 case 'H':        /* Turn off highlighting */
  508.                     gui_stop_highlight(arg1);
  509.                     break;
  510.                 case 'f':        /* flash the window (visual bell) */
  511.                     gui_mch_flash();
  512.                     break;
  513.                 default:
  514.                     p = s + 1;    /* Skip the ESC */
  515.                     break;
  516.             }
  517.             len -= ++p - s;
  518.             s = p;
  519.         }
  520.         else if (s[0] < 0x20)            /* Ctrl character, shouldn't happen */
  521.         {
  522.             /*
  523.              * For some reason vim sends me a ^M after hitting return on the
  524.              * ':' line.  Make sure we ignore this here.
  525.              */
  526.             len--;        /* Skip this char */
  527.             s++;
  528.         }
  529.         else
  530.         {
  531.             p = s;
  532.             while (len && *p >= 0x20)
  533.             {
  534.                 len--;
  535.                 p++;
  536.             }
  537.             gui_outstr(s, p - s);
  538.             s = p;
  539.         }
  540.     }
  541.     gui_update_cursor();
  542.     gui_update_scrollbars();
  543.     gui_update_horiz_scrollbar();
  544.  
  545.     /* 
  546.      * We need to make sure this is cleared since Athena doesn't tell us when
  547.      * he is done dragging.
  548.      */
  549.     gui.dragged_sb = SB_NONE;
  550.  
  551.     if (vim_strchr(p_guioptions, GO_ASEL) != NULL)
  552.         gui_update_selection();
  553.     gui_mch_flush();                /* In case vim decides to take a nap */
  554. }
  555.  
  556.     static void
  557. gui_outstr(s, len)
  558.     char_u    *s;
  559.     int        len;
  560. {
  561.     int        this_len;
  562.  
  563.     if (len == 0)
  564.         return;
  565.  
  566.     if (len < 0)
  567.         len = STRLEN(s);
  568.  
  569.     while (gui.col + len > Columns)
  570.     {
  571.         this_len = Columns - gui.col;
  572.         gui_mch_outstr_nowrap(s, this_len, TRUE, FALSE);
  573.         s += this_len;
  574.         len -= this_len;
  575.     }
  576.     gui_mch_outstr_nowrap(s, len, TRUE, FALSE);
  577. }
  578.  
  579. /*
  580.  * Un-draw the cursor.  Actually this just redraws the character at the given
  581.  * position.
  582.  */
  583.     void
  584. gui_undraw_cursor()
  585. {
  586.     if (IS_CURSOR_VALID())
  587.         gui_redraw_block(gui.cursor_row, gui.cursor_col, gui.cursor_row,
  588.                                                             gui.cursor_col);
  589. }
  590.  
  591.     void
  592. gui_redraw(x, y, w, h)
  593.     int        x;
  594.     int        y;
  595.     int        w;
  596.     int        h;
  597. {
  598.     int        row1, col1, row2, col2;
  599.  
  600.     row1 = Y_2_ROW(y);
  601.     col1 = X_2_COL(x);
  602.     row2 = Y_2_ROW(y + h - 1);
  603.     col2 = X_2_COL(x + w - 1);
  604.  
  605.     gui_redraw_block(row1, col1, row2, col2);
  606.  
  607.     /* We may need to redraw the cursor */
  608.     gui_update_cursor();
  609. }
  610.  
  611.     void
  612. gui_redraw_block(row1, col1, row2, col2)
  613.     int        row1;
  614.     int        col1;
  615.     int        row2;
  616.     int        col2;
  617. {
  618.     int        old_row, old_col;
  619.     long_u    old_hl_mask;
  620.     char_u    *screenp, *attrp, first_attr;
  621.     int        idx, len;
  622.  
  623.     /* Don't try to draw outside the window! */
  624.     /* Check everything, strange values may be caused by big border width */
  625.     col1 = check_col(col1);
  626.     col2 = check_col(col2);
  627.     row1 = check_row(row1);
  628.     row2 = check_row(row2);
  629.  
  630.     /* Don't try to update when NextScreen is not valid */
  631.     if (!screen_cleared || NextScreen == NULL)
  632.         return;
  633.  
  634.     /* Remember where our cursor was */
  635.     old_row = gui.row;
  636.     old_col = gui.col;
  637.     old_hl_mask = gui.highlight_mask;
  638.  
  639.     for (gui.row = row1; gui.row <= row2; gui.row++)
  640.     {
  641.         gui.col = col1;
  642.         screenp = LinePointers[gui.row] + gui.col;
  643.         attrp = screenp + Columns;
  644.         len = col2 - col1 + 1;
  645.         while (len > 0)
  646.         {
  647.             switch (attrp[0])
  648.             {
  649.                 case CHAR_INVERT:
  650.                     gui.highlight_mask = HL_INVERSE;
  651.                     break;
  652.                 case CHAR_UNDERL:
  653.                     gui.highlight_mask = HL_UNDERLINE;
  654.                     break;
  655.                 case CHAR_BOLD:
  656.                     gui.highlight_mask = HL_BOLD;
  657.                     break;
  658.                 case CHAR_STDOUT:
  659.                     gui.highlight_mask = HL_STANDOUT;
  660.                     break;
  661.                 case CHAR_ITALIC:
  662.                     gui.highlight_mask = HL_ITAL;
  663.                     break;
  664.                 case CHAR_NORMAL:
  665.                 default:
  666.                     gui.highlight_mask = HL_NORMAL;
  667.                     break;
  668.             }
  669.             first_attr = attrp[0];
  670.             for (idx = 0; len > 0 && attrp[idx] == first_attr; idx++)
  671.                 --len;
  672.             gui_mch_outstr_nowrap(screenp, idx, FALSE, FALSE);
  673.             screenp += idx;
  674.             attrp += idx;
  675.         }
  676.     }
  677.  
  678.     /* Put the cursor back where it was */
  679.     gui.row = old_row;
  680.     gui.col = old_col;
  681.     gui.highlight_mask = old_hl_mask;
  682. }
  683.  
  684. /*
  685.  * Check bounds for column number
  686.  */
  687.     int
  688. check_col(col)
  689.     int        col;
  690. {
  691.     if (col < 0)
  692.         return 0;
  693.     if (col >= (int)Columns)
  694.         return (int)Columns - 1;
  695.     return col;
  696. }
  697.  
  698. /*
  699.  * Check bounds for row number
  700.  */
  701.     int
  702. check_row(row)
  703.     int        row;
  704. {
  705.     if (row < 0)
  706.         return 0;
  707.     if (row >= (int)Rows)
  708.         return (int)Rows - 1;
  709.     return row;
  710. }
  711.  
  712. /*
  713.  * Generic mouse support function.  Add a mouse event to the input buffer with
  714.  * the given properties.
  715.  *    button            --- may be any of MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT,
  716.  *                        MOUSE_DRAG, or MOUSE_RELEASE.
  717.  *    x, y            --- Coordinates of mouse in pixels.
  718.  *    repeated_click    --- TRUE if this click comes only a short time after a
  719.  *                        previous click.
  720.  *    modifiers        --- Bit field which may be any of the following modifiers
  721.  *                        or'ed together: MOUSE_SHIFT | MOUSE_CTRL | MOUSE_ALT.
  722.  * This function will ignore drag events where the mouse has not moved to a new
  723.  * character.
  724.  */
  725.     void
  726. gui_send_mouse_event(button, x, y, repeated_click, modifiers)
  727.     int        button;
  728.     int        x;
  729.     int        y;
  730.     int        repeated_click;
  731.     int_u    modifiers;
  732. {
  733.     static int        prev_row = 0, prev_col = 0;
  734.     static int        prev_button = -1;
  735.     static linenr_t prev_topline = 0;
  736.     static int        num_clicks = 1;
  737.     char_u            string[6];
  738.     int                row, col;
  739.  
  740.     row = Y_2_ROW(y);
  741.     col = X_2_COL(x);
  742.  
  743.     /*
  744.      * If we are dragging and the mouse hasn't moved far enough to be on a
  745.      * different character, then don't send an event to vim.
  746.      */
  747.     if (button == MOUSE_DRAG && row == prev_row && col == prev_col)
  748.         return;
  749.  
  750.     /*
  751.      * If topline has changed (window scrolled) since the last click, reset
  752.      * repeated_click, because we don't want starting Visual mode when
  753.      * clicking on a different character in the text.
  754.      */
  755.     if (curwin->w_topline != prev_topline)
  756.         repeated_click = FALSE;
  757.  
  758.     string[0] = CSI;    /* this sequence is recognized by check_termcode() */
  759.     string[1] = KS_MOUSE;
  760.     string[2] = K_FILLER;
  761.     if (button != MOUSE_DRAG && button != MOUSE_RELEASE)
  762.     {
  763.         if (repeated_click)
  764.         {
  765.             /*
  766.              * Handle multiple clicks.  They only count if the mouse is still
  767.              * pointing at the same character.
  768.              */
  769.             if (button != prev_button || row != prev_row || col != prev_col)
  770.                 num_clicks = 1;
  771.             else if (++num_clicks > 4)
  772.                 num_clicks = 1;
  773.         }
  774.         else
  775.             num_clicks = 1;
  776.         prev_button = button;
  777.         prev_topline = curwin->w_topline;
  778.  
  779.         string[3] = (char_u)(button | 0x20);
  780.         SET_NUM_MOUSE_CLICKS(string[3], num_clicks);
  781.     }
  782.     else
  783.         string[3] = (char_u)button;
  784.  
  785.     string[3] |= modifiers;
  786.     string[4] = (char_u)(col + ' ' + 1);
  787.     string[5] = (char_u)(row + ' ' + 1);
  788.     add_to_input_buf(string, 6);
  789.  
  790.     prev_row = row;
  791.     prev_col = col;
  792. }
  793.  
  794. /*
  795.  * Selection stuff, for cutting and pasting text to other windows.
  796.  */
  797.  
  798. /*
  799.  * Check whether the VIsual area has changed, and if so try to become the owner
  800.  * of the selection, and free any old converted selection we may still have
  801.  * lying around.  If the VIsual mode has ended, make a copy of what was
  802.  * selected so we can still give it to others.  Will probably have to make sure
  803.  * this is called whenever VIsual mode is ended.
  804.  */
  805.     static void
  806. gui_update_selection()
  807. {
  808.     /* If visual mode is only due to a redo command ("."), then ignore it */
  809.     if (redo_VIsual_busy)
  810.         return;
  811.     if (!VIsual_active)
  812.     {
  813.         gui_mch_clear_selection();
  814.         gui.selection.start = gui.selection.end = VIsual;
  815.     }
  816.     else if (lt(VIsual, curwin->w_cursor))
  817.     {
  818.         if (!equal(gui.selection.start, VIsual) ||
  819.             !equal(gui.selection.end, curwin->w_cursor))
  820.         {
  821.             gui_mch_clear_selection();
  822.             gui.selection.start = VIsual;
  823.             gui.selection.end = curwin->w_cursor;
  824.             gui_free_selection();
  825.             gui_own_selection();
  826.         }
  827.     }
  828.     else
  829.     {
  830.         if (!equal(gui.selection.start, curwin->w_cursor) ||
  831.             !equal(gui.selection.end, VIsual))
  832.         {
  833.             gui_mch_clear_selection();
  834.             gui.selection.start = curwin->w_cursor;
  835.             gui.selection.end = VIsual;
  836.             gui_free_selection();
  837.             gui_own_selection();
  838.         }
  839.     }
  840. }
  841.  
  842.     void
  843. gui_own_selection()
  844. {
  845.     /*
  846.      * Also want to check somehow that we are reading from the keyboard rather
  847.      * than a mapping etc.
  848.      */
  849.     if (!gui.selection.owned && gui_mch_own_selection())
  850.     {
  851.         gui_free_selection();
  852.         gui.selection.owned = TRUE;
  853.     }
  854. }
  855.  
  856.     void
  857. gui_lose_selection()
  858. {
  859.     gui_free_selection();
  860.     gui.selection.owned = FALSE;
  861.     gui_mch_lose_selection();
  862. }
  863.  
  864.     void
  865. gui_copy_selection()
  866. {
  867.     if (VIsual_active)
  868.     {
  869.         if (vim_strchr(p_guioptions, GO_ASEL) == NULL)
  870.             gui_update_selection();
  871.         gui_own_selection();
  872.         if (gui.selection.owned)
  873.             gui_get_selection();
  874.     }
  875. }
  876.  
  877.     void
  878. gui_auto_select()
  879. {
  880.     if (vim_strchr(p_guioptions, GO_ASEL) != NULL)
  881.         gui_copy_selection();
  882. }
  883.  
  884. /*
  885.  * Menu stuff.
  886.  */
  887.  
  888.     void
  889. gui_menu_cb(menu)
  890.     GuiMenu    *menu;
  891. {
  892.     char_u    bytes[3 + sizeof(long_u)];
  893.  
  894.     bytes[0] = CSI;
  895.     bytes[1] = KS_MENU;
  896.     bytes[2] = K_FILLER;
  897.     add_long_to_buf((long_u)menu, bytes + 3);
  898.     add_to_input_buf(bytes, 3 + sizeof(long_u));
  899. }
  900.  
  901. /*
  902.  * Return the index into the menu->strings or menu->noremap arrays for the
  903.  * current state.  Returns MENU_INDEX_INVALID if there is no mapping for the
  904.  * given menu in the current mode.
  905.  */
  906.     int
  907. gui_get_menu_index(menu, state)
  908.     GuiMenu    *menu;
  909.     int        state;
  910. {
  911.     int        idx;
  912.  
  913.     if (VIsual_active)
  914.         idx = MENU_INDEX_VISUAL;
  915.     else if ((state & NORMAL))
  916.         idx = MENU_INDEX_NORMAL;
  917.     else if ((state & INSERT))
  918.         idx = MENU_INDEX_INSERT;
  919.     else if ((state & CMDLINE))
  920.         idx = MENU_INDEX_CMDLINE;
  921.     else
  922.         idx = MENU_INDEX_INVALID;
  923.  
  924.     if (idx != MENU_INDEX_INVALID && menu->strings[idx] == NULL)
  925.         idx = MENU_INDEX_INVALID;
  926.     return idx;
  927. }
  928.  
  929. /*
  930.  * Return the modes specified by the given menu command (eg :menu! returns
  931.  * MENU_CMDLINE_MODE | MENU_INSERT_MODE).  If noremap is not NULL, then the
  932.  * flag it points to is set according to whether the command is a "nore"
  933.  * command.  If unmenu is not NULL, then the flag it points to is set
  934.  * according to whether the command is an "unmenu" command.
  935.  */
  936.     static int
  937. gui_get_menu_cmd_modes(cmd, force, noremap, unmenu)
  938.     char_u    *cmd;
  939.     int        force;        /* Was there a "!" after the command? */
  940.     int        *noremap;
  941.     int        *unmenu;
  942. {
  943.     int        modes = 0x0;
  944.  
  945.     if (*cmd == 'n' && cmd[1] != 'o')    /* nmenu, nnoremenu */
  946.     {
  947.         modes |= MENU_NORMAL_MODE;
  948.         cmd++;
  949.     }
  950.     else if (*cmd == 'v')                /* vmenu, vnoremenu */
  951.     {
  952.         modes |= MENU_VISUAL_MODE;
  953.         cmd++;
  954.     }
  955.     else if (*cmd == 'i')                /* imenu, inoremenu */
  956.     {
  957.         modes |= MENU_INSERT_MODE;
  958.         cmd++;
  959.     }
  960.     else if (*cmd == 'c')                /* cmenu, cnoremenu */
  961.     {
  962.         modes |= MENU_CMDLINE_MODE;
  963.         cmd++;
  964.     }
  965.     else if (force)                    /* menu!, noremenu! */
  966.         modes |= MENU_INSERT_MODE | MENU_CMDLINE_MODE;
  967.     else                            /* menu, noremenu */
  968.         modes |= MENU_NORMAL_MODE | MENU_VISUAL_MODE;
  969.  
  970.     if (noremap != NULL)
  971.         *noremap = (*cmd == 'n');
  972.     if (unmenu != NULL)
  973.         *unmenu = (*cmd == 'u');
  974.     return modes;
  975. }
  976.  
  977. /*
  978.  * Do the :menu commands.
  979.  */
  980.     void
  981. gui_do_menu(cmd, arg, force)
  982.     char_u    *cmd;
  983.     char_u    *arg;
  984.     int        force;
  985. {
  986.     char_u    *menu_path;
  987.     int        modes;
  988.     char_u    *map_to;
  989.     int        noremap;
  990.     int        unmenu;
  991.     char_u    *map_buf;
  992.  
  993.     modes = gui_get_menu_cmd_modes(cmd, force, &noremap, &unmenu);
  994.     menu_path = arg;
  995.     if (*menu_path == NUL)
  996.     {
  997.         gui_show_menus(menu_path, modes);
  998.         return;
  999.     }
  1000.     while (*arg && !vim_iswhite(*arg))
  1001.     {
  1002.         if ((*arg == '\\' || *arg == Ctrl('V')) && arg[1] != NUL)
  1003.             arg++;
  1004.         arg++;
  1005.     }
  1006.     if (*arg != NUL)
  1007.         *arg++ = NUL;
  1008.     arg = skipwhite(arg);
  1009.     map_to = arg;
  1010.     if (*map_to == NUL && !unmenu)
  1011.     {
  1012.         gui_show_menus(menu_path, modes);
  1013.         return;
  1014.     }
  1015.     else if (*map_to != NUL && unmenu)
  1016.     {
  1017.         EMSG("Trailing characters");
  1018.         return;
  1019.     }
  1020.     if (unmenu)
  1021.     {
  1022.         if (STRCMP(menu_path, "*") == 0)        /* meaning: remove all menus */
  1023.             menu_path = (char_u *)"";
  1024.         gui_remove_menu(&gui.root_menu, menu_path, modes);
  1025.     }
  1026.     else
  1027.     {
  1028.         /* Replace special key codes */
  1029.         map_to = replace_termcodes(map_to, &map_buf, FALSE);
  1030.         gui_add_menu_path(menu_path, modes, gui_menu_cb, map_to, noremap);
  1031.         vim_free(map_buf);
  1032.     }
  1033. }
  1034.  
  1035. /*
  1036.  * Add the menu with the given name to the menu hierarchy
  1037.  */
  1038.     static int
  1039. gui_add_menu_path(path_name, modes, call_back, call_data, noremap)
  1040.     char_u    *path_name;
  1041.     int        modes;
  1042.     void    (*call_back)();
  1043.     char_u    *call_data;
  1044.     int        noremap;
  1045. {
  1046.     GuiMenu    **menup;
  1047.     GuiMenu    *menu = NULL;
  1048.     GuiMenu    *parent;
  1049.     char_u    *p;
  1050.     char_u    *name;
  1051.     int        i;
  1052.  
  1053.     /* Make a copy so we can stuff around with it, since it could be const */
  1054.     path_name = strsave(path_name);
  1055.     if (path_name == NULL)
  1056.         return FAIL;
  1057.     menup = &gui.root_menu;
  1058.     parent = NULL;
  1059.     name = path_name;
  1060.     while (*name)
  1061.     {
  1062.         /* Get name of this element in the menu hierarchy */
  1063.         p = gui_menu_name_skip(name);
  1064.  
  1065.         /* See if it's already there */
  1066.         menu = *menup;
  1067.         while (menu != NULL)
  1068.         {
  1069.             if (STRCMP(name, menu->name) == 0)
  1070.             {
  1071.                 if (*p == NUL && menu->children != NULL)
  1072.                 {
  1073.                     EMSG("Menu path must not lead to a sub-menu");
  1074.                     vim_free(path_name);
  1075.                     return FAIL;
  1076.                 }
  1077.                 else if (*p != NUL && menu->children == NULL)
  1078.                 {
  1079.                     EMSG("Part of menu-item path is not sub-menu");
  1080.                     vim_free(path_name);
  1081.                     return FAIL;
  1082.                 }
  1083.                 break;
  1084.             }
  1085.             menup = &menu->next;
  1086.             menu = menu->next;
  1087.         }
  1088.         if (menu == NULL)
  1089.         {
  1090.             if (*p == NUL && parent == NULL)
  1091.             {
  1092.                 EMSG("Must not add menu items directly to menu bar");
  1093.                 vim_free(path_name);
  1094.                 return FAIL;
  1095.             }
  1096.  
  1097.             /* Not already there, so lets add it */
  1098.             menu = (GuiMenu *)alloc(sizeof(GuiMenu));
  1099.             if (menu == NULL)
  1100.             {
  1101.                 vim_free(path_name);
  1102.                 return FAIL;
  1103.             }
  1104.             menu->modes = modes;
  1105.             menu->name = strsave(name);
  1106.             menu->cb = NULL;
  1107.             for (i = 0; i < 4; i++)
  1108.             {
  1109.                 menu->strings[i] = NULL;
  1110.                 menu->noremap[i] = FALSE;
  1111.             }
  1112.             menu->children = NULL;
  1113.             menu->next = NULL;
  1114.             if (gui.in_use)     /* Otherwise it will be added when GUI starts */
  1115.             {
  1116.                 if (*p == NUL)
  1117.                 {
  1118.                     /* Real menu item, not sub-menu */
  1119.                     gui_mch_add_menu_item(menu, parent);
  1120.  
  1121.                     /* Want to update menus now even if mode not changed */
  1122.                     force_menu_update = TRUE;
  1123.                 }
  1124.                 else
  1125.                 {
  1126.                     /* Sub-menu (not at end of path yet) */
  1127.                     gui_mch_add_menu(menu, parent);
  1128.                 }
  1129.             }
  1130.             *menup = menu;
  1131.         }
  1132.         else
  1133.         {
  1134.             /*
  1135.              * If this menu option was previously only available in other
  1136.              * modes, then make sure it's available for this one now
  1137.              */
  1138.             menu->modes |= modes;
  1139.         }
  1140.  
  1141.         menup = &menu->children;
  1142.         parent = menu;
  1143.         name = p;
  1144.     }
  1145.     vim_free(path_name);
  1146.  
  1147.     if (menu != NULL)
  1148.     {
  1149.         menu->cb = call_back;
  1150.         p = (call_data == NULL) ? NULL : strsave(call_data);
  1151.  
  1152.         /* May match more than one of these */
  1153.         if (modes & MENU_NORMAL_MODE)
  1154.         {
  1155.             gui_free_menu_string(menu, MENU_INDEX_NORMAL);
  1156.             menu->strings[MENU_INDEX_NORMAL] = p;
  1157.             menu->noremap[MENU_INDEX_NORMAL] = noremap;
  1158.         }
  1159.         if (modes & MENU_VISUAL_MODE)
  1160.         {
  1161.             gui_free_menu_string(menu, MENU_INDEX_VISUAL);
  1162.             menu->strings[MENU_INDEX_VISUAL] = p;
  1163.             menu->noremap[MENU_INDEX_VISUAL] = noremap;
  1164.         }
  1165.         if (modes & MENU_INSERT_MODE)
  1166.         {
  1167.             gui_free_menu_string(menu, MENU_INDEX_INSERT);
  1168.             menu->strings[MENU_INDEX_INSERT] = p;
  1169.             menu->noremap[MENU_INDEX_INSERT] = noremap;
  1170.         }
  1171.         if (modes & MENU_CMDLINE_MODE)
  1172.         {
  1173.             gui_free_menu_string(menu, MENU_INDEX_CMDLINE);
  1174.             menu->strings[MENU_INDEX_CMDLINE] = p;
  1175.             menu->noremap[MENU_INDEX_CMDLINE] = noremap;
  1176.         }
  1177.     }
  1178.     return OK;
  1179. }
  1180.  
  1181. /*
  1182.  * Remove the (sub)menu with the given name from the menu hierarchy
  1183.  * Called recursively.
  1184.  */
  1185.     static int
  1186. gui_remove_menu(menup, name, modes)
  1187.     GuiMenu    **menup;
  1188.     char_u    *name;
  1189.     int        modes;
  1190. {
  1191.     GuiMenu    *menu;
  1192.     GuiMenu    *child;
  1193.     char_u    *p;
  1194.  
  1195.     if (*menup == NULL)
  1196.         return OK;            /* Got to bottom of hierarchy */
  1197.  
  1198.     /* Get name of this element in the menu hierarchy */
  1199.     p = gui_menu_name_skip(name);
  1200.  
  1201.     /* Find the menu */
  1202.     menu = *menup;
  1203.     while (menu != NULL)
  1204.     {
  1205.         if (*name == NUL || STRCMP(name, menu->name) == 0)
  1206.         {
  1207.             if (*p != NUL && menu->children == NULL)
  1208.             {
  1209.                 EMSG("Part of menu-item path is not sub-menu");
  1210.                 return FAIL;
  1211.             }
  1212.             if ((menu->modes & modes) != 0x0)
  1213.             {
  1214.                 if (gui_remove_menu(&menu->children, p, modes) == FAIL)
  1215.                     return FAIL;
  1216.             }
  1217.             else if (*name != NUL)
  1218.             {
  1219.                 EMSG("Menu only exists in another mode");
  1220.                 return FAIL;
  1221.             }
  1222.  
  1223.             /*
  1224.              * When name is empty, we are removing all menu items for the given
  1225.              * modes, so keep looping, otherwise we are just removing the named
  1226.              * menu item (which has been found) so break here.
  1227.              */
  1228.             if (*name != NUL)
  1229.                 break;
  1230.  
  1231.             /* Remove the menu item for the given mode[s] */
  1232.             menu->modes &= ~modes;
  1233.  
  1234.             if (menu->modes == 0x0)
  1235.             {
  1236.                 /* The menu item is no longer valid in ANY mode, so delete it */
  1237.                 *menup = menu->next;
  1238.                 gui_free_menu(menu);
  1239.             }
  1240.             else
  1241.                 menup = &menu->next;
  1242.         }
  1243.         else
  1244.             menup = &menu->next;
  1245.         menu = *menup;
  1246.     }
  1247.     if (*name != NUL)
  1248.     {
  1249.         if (menu == NULL)
  1250.         {
  1251.             EMSG("No menu of that name");
  1252.             return FAIL;
  1253.         }
  1254.  
  1255.         /* Recalculate modes for menu based on the new updated children */
  1256.         menu->modes = 0x0;
  1257.         for (child = menu->children; child != NULL; child = child->next)
  1258.             menu->modes |= child->modes;
  1259.         if (menu->modes == 0x0)
  1260.         {
  1261.             /* The menu item is no longer valid in ANY mode, so delete it */
  1262.             *menup = menu->next;
  1263.             gui_free_menu(menu);
  1264.         }
  1265.     }
  1266.  
  1267.     return OK;
  1268. }
  1269.  
  1270. /*
  1271.  * Free the given menu structure
  1272.  */
  1273.     static void
  1274. gui_free_menu(menu)
  1275.     GuiMenu    *menu;
  1276. {
  1277.     int        i;
  1278.  
  1279.     gui_mch_destroy_menu(menu);        /* Free machine specific menu structures */
  1280.     vim_free(menu->name);
  1281.     for (i = 0; i < 4; i++)
  1282.         gui_free_menu_string(menu, i);
  1283.     vim_free(menu);
  1284.  
  1285.     /* Want to update menus now even if mode not changed */
  1286.     force_menu_update = TRUE;
  1287. }
  1288.  
  1289. /*
  1290.  * Free the menu->string with the given index.
  1291.  */
  1292.     static void
  1293. gui_free_menu_string(menu, idx)
  1294.     GuiMenu    *menu;
  1295.     int        idx;
  1296. {
  1297.     int        count = 0;
  1298.     int        i;
  1299.  
  1300.     for (i = 0; i < 4; i++)
  1301.         if (menu->strings[i] == menu->strings[idx])
  1302.             count++;
  1303.     if (count == 1)
  1304.         vim_free(menu->strings[idx]);
  1305.     menu->strings[idx] = NULL;
  1306. }
  1307.  
  1308. /*
  1309.  * Show the mapping associated with a menu item or hierarchy in a sub-menu.
  1310.  */
  1311.     static int
  1312. gui_show_menus(path_name, modes)
  1313.     char_u    *path_name;
  1314.     int        modes;
  1315. {
  1316.     char_u    *p;
  1317.     char_u    *name;
  1318.     GuiMenu    *menu;
  1319.     GuiMenu    *parent = NULL;
  1320.  
  1321.     menu = gui.root_menu;
  1322.     name = path_name = strsave(path_name);
  1323.     if (path_name == NULL)
  1324.         return FAIL;
  1325.  
  1326.     /* First, find the (sub)menu with the given name */
  1327.     while (*name)
  1328.     {
  1329.         p = gui_menu_name_skip(name);
  1330.         while (menu != NULL)
  1331.         {
  1332.             if (STRCMP(name, menu->name) == 0)
  1333.             {
  1334.                 /* Found menu */
  1335.                 if (*p != NUL && menu->children == NULL)
  1336.                 {
  1337.                     EMSG("Part of menu-item path is not sub-menu");
  1338.                     vim_free(path_name);
  1339.                     return FAIL;
  1340.                 }
  1341.                 else if ((menu->modes & modes) == 0x0)
  1342.                 {
  1343.                     EMSG("Menu only exists in another mode");
  1344.                     vim_free(path_name);
  1345.                     return FAIL;
  1346.                 }
  1347.                 break;
  1348.             }
  1349.             menu = menu->next;
  1350.         }
  1351.         if (menu == NULL)
  1352.         {
  1353.             EMSG("No menu of that name");
  1354.             vim_free(path_name);
  1355.             return FAIL;
  1356.         }
  1357.         name = p;
  1358.         parent = menu;
  1359.         menu = menu->children;
  1360.     }
  1361.  
  1362.     /* Now we have found the matching menu, and we list the mappings */
  1363.     set_highlight('t');        /* Highlight title */
  1364.     start_highlight();
  1365.     MSG_OUTSTR("\n--- Menus ---");
  1366.     stop_highlight();
  1367.  
  1368.     gui_show_menus_recursive(parent, modes, 0);
  1369.     return OK;
  1370. }
  1371.  
  1372. /*
  1373.  * Recursively show the mappings associated with the menus under the given one
  1374.  */
  1375.     static void
  1376. gui_show_menus_recursive(menu, modes, depth)
  1377.     GuiMenu    *menu;
  1378.     int        modes;
  1379.     int        depth;
  1380. {
  1381.     int        i;
  1382.     int        bit;
  1383.  
  1384.     if (menu != NULL && (menu->modes & modes) == 0x0)
  1385.         return;
  1386.  
  1387.     if (menu != NULL)
  1388.     {
  1389.         msg_outchar('\n');
  1390.         if (got_int)            /* "q" hit for "--more--" */
  1391.             return;
  1392.         for (i = 0; i < depth; i++)
  1393.             MSG_OUTSTR("  ");
  1394.         set_highlight('d');            /* Same as for directories!? */
  1395.         start_highlight();
  1396.         msg_outstr(menu->name);
  1397.         stop_highlight();
  1398.     }
  1399.  
  1400.     if (menu != NULL && menu->children == NULL)
  1401.     {
  1402.         for (bit = 0; bit < 4; bit++)
  1403.             if ((menu->modes & modes & (1 << bit)) != 0)
  1404.             {
  1405.                 msg_outchar('\n');
  1406.                 if (got_int)            /* "q" hit for "--more--" */
  1407.                     return;
  1408.                 for (i = 0; i < depth + 2; i++)
  1409.                     MSG_OUTSTR("  ");
  1410.                 msg_outchar("nvic"[bit]);
  1411.                 if (menu->noremap[bit])
  1412.                     msg_outchar('*');
  1413.                 else
  1414.                     msg_outchar(' ');
  1415.                 MSG_OUTSTR("  ");
  1416.                 msg_outtrans_special(menu->strings[bit], TRUE);
  1417.             }
  1418.     }
  1419.     else
  1420.     {
  1421.         if (menu == NULL)
  1422.         {
  1423.             menu = gui.root_menu;
  1424.             depth--;
  1425.         }
  1426.         else
  1427.             menu = menu->children;
  1428.         for (; menu != NULL; menu = menu->next)
  1429.             gui_show_menus_recursive(menu, modes, depth + 1);
  1430.     }
  1431. }
  1432.  
  1433. /*
  1434.  * Used when expanding menu names.
  1435.  */
  1436. static GuiMenu    *expand_menu = NULL;
  1437. static int        expand_modes = 0x0;
  1438.  
  1439. /*
  1440.  * Work out what to complete when doing command line completion of menu names.
  1441.  */
  1442.     char_u *
  1443. gui_set_context_in_menu_cmd(cmd, arg, force)
  1444.     char_u    *cmd;
  1445.     char_u    *arg;
  1446.     int        force;
  1447. {
  1448.     char_u    *after_dot;
  1449.     char_u    *p;
  1450.     char_u    *path_name = NULL;
  1451.     char_u    *name;
  1452.     int        unmenu;
  1453.     GuiMenu    *menu;
  1454.  
  1455.     expand_context = EXPAND_UNSUCCESSFUL;
  1456.  
  1457.     after_dot = arg;
  1458.     for (p = arg; *p && !vim_iswhite(*p); ++p)
  1459.     {
  1460.         if ((*p == '\\' || *p == Ctrl('V')) && p[1] != NUL)
  1461.             p++;
  1462.         else if (*p == '.')
  1463.             after_dot = p + 1;
  1464.     }
  1465.     if (*p == NUL)                /* Complete the menu name */
  1466.     {
  1467.         /*
  1468.          * With :unmenu, you only want to match menus for the appropriate mode.
  1469.          * With :menu though you might want to add a menu with the same name as
  1470.          * one in another mode, so match menus fom other modes too.
  1471.          */
  1472.         expand_modes = gui_get_menu_cmd_modes(cmd, force, NULL, &unmenu);
  1473.         if (!unmenu)
  1474.             expand_modes = MENU_ALL_MODES;
  1475.  
  1476.         menu = gui.root_menu;
  1477.         if (after_dot != arg)
  1478.         {
  1479.             path_name = alloc(after_dot - arg);
  1480.             if (path_name == NULL)
  1481.                 return NULL;
  1482.             STRNCPY(path_name, arg, after_dot - arg - 1);
  1483.             path_name[after_dot - arg - 1] = NUL;
  1484.         }
  1485.         name = path_name;
  1486.         while (name != NULL && *name)
  1487.         {
  1488.             p = gui_menu_name_skip(name);
  1489.             while (menu != NULL)
  1490.             {
  1491.                 if (STRCMP(name, menu->name) == 0)
  1492.                 {
  1493.                     /* Found menu */
  1494.                     if ((*p != NUL && menu->children == NULL)
  1495.                         || ((menu->modes & expand_modes) == 0x0))
  1496.                     {
  1497.                         /*
  1498.                          * Menu path continues, but we have reached a leaf.
  1499.                          * Or menu exists only in another mode.
  1500.                          */
  1501.                         vim_free(path_name);
  1502.                         return NULL;
  1503.                     }
  1504.                     break;
  1505.                 }
  1506.                 menu = menu->next;
  1507.             }
  1508.             if (menu == NULL)
  1509.             {
  1510.                 /* No menu found with the name we were looking for */
  1511.                 vim_free(path_name);
  1512.                 return NULL;
  1513.             }
  1514.             name = p;
  1515.             menu = menu->children;
  1516.         }
  1517.  
  1518.         expand_context = EXPAND_MENUS;
  1519.         expand_pattern = after_dot;
  1520.         expand_menu = menu;
  1521.     }
  1522.     else                        /* We're in the mapping part */
  1523.         expand_context = EXPAND_NOTHING;
  1524.     return NULL;
  1525. }
  1526.  
  1527. /*
  1528.  * Expand the menu names.
  1529.  */
  1530.     int
  1531. gui_ExpandMenuNames(prog, num_file, file)
  1532.     regexp    *prog;
  1533.     int        *num_file;
  1534.     char_u    ***file;
  1535. {
  1536.     GuiMenu    *menu;
  1537.     int        round;
  1538.     int        count;
  1539.  
  1540.     /*
  1541.      * round == 1: Count the matches.
  1542.      * round == 2: Save the matches into the array.
  1543.      */
  1544.     for (round = 1; round <= 2; ++round)
  1545.     {
  1546.         count = 0;
  1547.         for (menu = expand_menu; menu != NULL; menu = menu->next)
  1548.             if ((menu->modes & expand_modes) != 0x0
  1549.                 && vim_regexec(prog, menu->name, TRUE))
  1550.             {
  1551.                 if (round == 1)
  1552.                     count++;
  1553.                 else
  1554.                     (*file)[count++] = strsave_escaped(menu->name,
  1555.                                                        (char_u *)" \t\\.");
  1556.             }
  1557.         if (round == 1)
  1558.         {
  1559.             *num_file = count;
  1560.             if (count == 0 || (*file = (char_u **)
  1561.                          alloc((unsigned)(count * sizeof(char_u *)))) == NULL)
  1562.                 return FAIL;
  1563.         }
  1564.     }
  1565.     return OK;
  1566. }
  1567.  
  1568. /*
  1569.  * Skip over this element of the menu path and return the start of the next
  1570.  * element.  Any \ and ^Vs are removed from the current element.
  1571.  */
  1572.     static char_u *
  1573. gui_menu_name_skip(name)
  1574.     char_u    *name;
  1575. {
  1576.     char_u    *p;
  1577.  
  1578.     for (p = name; *p && *p != '.'; p++)
  1579.         if (*p == '\\' || *p == Ctrl('V'))
  1580.         {
  1581.             STRCPY(p, p + 1);
  1582.             if (*p == NUL)
  1583.                 break;
  1584.         }
  1585.     if (*p)
  1586.         *p++ = NUL;
  1587.     return p;
  1588. }
  1589.  
  1590. /*
  1591.  * After we have started the GUI, then we can create any menus that have been
  1592.  * defined.  This is done once here.  gui_add_menu_path() may have already been
  1593.  * called to define these menus, and may be called again.  This function calls
  1594.  * itself recursively.    Should be called at the top level with:
  1595.  * gui_create_initial_menus(gui.root_menu, NULL);
  1596.  */
  1597.     static void
  1598. gui_create_initial_menus(menu, parent)
  1599.     GuiMenu    *menu;
  1600.     GuiMenu    *parent;
  1601. {
  1602.     while (menu)
  1603.     {
  1604.         if (menu->children != NULL)
  1605.         {
  1606.             gui_mch_add_menu(menu, parent);
  1607.             gui_create_initial_menus(menu->children, menu);
  1608.         }
  1609.         else
  1610.             gui_mch_add_menu_item(menu, parent);
  1611.         menu = menu->next;
  1612.     }
  1613. }
  1614.  
  1615.  
  1616. /*
  1617.  * Set which components are present.
  1618.  * If "oldval" is not NULL, "oldval" is the previous value, the new * value is
  1619.  * in p_guioptions.
  1620.  */
  1621.     void
  1622. gui_init_which_components(oldval)
  1623.     char_u    *oldval;
  1624. {
  1625.     char_u    *p;
  1626.     int        i;
  1627.     int        grey_old, grey_new;
  1628.     char_u    *temp;
  1629.  
  1630.     if (oldval != NULL)
  1631.     {
  1632.         /*
  1633.          * Check if the menu's go from grey to non-grey or vise versa.
  1634.          */
  1635.         grey_old = (vim_strchr(oldval, GO_GREY) != NULL);
  1636.         grey_new = (vim_strchr(p_guioptions, GO_GREY) != NULL);
  1637.         if (grey_old != grey_new)
  1638.         {
  1639.             temp = p_guioptions;
  1640.             p_guioptions = oldval;
  1641.             gui_x11_update_menus(MENU_ALL_MODES);
  1642.             p_guioptions = temp;
  1643.         }
  1644.     }
  1645.  
  1646.     gui.menu_is_active = FALSE;
  1647.     for (i = 0; i < 3; i++)
  1648.         gui.which_scrollbars[i] = FALSE;
  1649.     for (p = p_guioptions; *p; p++)
  1650.         switch (*p)
  1651.         {
  1652.             case GO_LEFT:
  1653.                 gui.which_scrollbars[SB_LEFT] = TRUE;
  1654.                 break;
  1655.             case GO_RIGHT:
  1656.                 gui.which_scrollbars[SB_RIGHT] = TRUE;
  1657.                 break;
  1658.             case GO_BOT:
  1659.                 gui.which_scrollbars[SB_BOTTOM] = TRUE;
  1660.                 break;
  1661.             case GO_MENUS:
  1662.                 gui.menu_is_active = TRUE;
  1663.                 break;
  1664.             case GO_GREY:
  1665.                 /* make menu's have grey items, ignored here */
  1666.                 break;
  1667.             default:
  1668.                 /* Should give error message for internal error */
  1669.                 break;
  1670.         }
  1671.     if (gui.in_use)
  1672.         gui_mch_create_which_components();
  1673. }
  1674.  
  1675.  
  1676. /*
  1677.  * Vertical scrollbar stuff:
  1678.  */
  1679.  
  1680.     static void
  1681. gui_update_scrollbars()
  1682. {
  1683.     WIN        *wp;
  1684.     int        worst_update = SB_UPDATE_NOTHING;
  1685.     int        val, size, max;
  1686.     int        which_sb;
  1687.     int        cmdline_height;
  1688.  
  1689.     /*
  1690.      * Don't want to update a scrollbar while we're dragging it.  But if we
  1691.      * have both a left and right scrollbar, and we drag one of them, we still
  1692.      * need to update the other one.
  1693.      */
  1694.     if ((gui.dragged_sb == SB_LEFT || gui.dragged_sb == SB_RIGHT) &&
  1695.             (!gui.which_scrollbars[SB_LEFT] || !gui.which_scrollbars[SB_RIGHT]))
  1696.         return;
  1697.  
  1698.     if (gui.dragged_sb == SB_LEFT || gui.dragged_sb == SB_RIGHT)
  1699.     {
  1700.         /*
  1701.          * If we have two scrollbars and one of them is being dragged, just
  1702.          * copy the scrollbar position from the dragged one to the other one.
  1703.          */
  1704.         which_sb = SB_LEFT + SB_RIGHT - gui.dragged_sb;
  1705.         if (gui.dragged_wp != NULL)
  1706.             gui.dragged_wp->w_scrollbar.update[which_sb] = SB_UPDATE_VALUE;
  1707.         else
  1708.             gui.cmdline_sb.update[which_sb] = SB_UPDATE_VALUE;
  1709.  
  1710.         gui_mch_update_scrollbars(SB_UPDATE_VALUE, which_sb);
  1711.         return;
  1712.     }
  1713.  
  1714.     /* Return straight away if there is neither a left nor right scrollbar */
  1715.     if (!gui.which_scrollbars[SB_LEFT] && !gui.which_scrollbars[SB_RIGHT])
  1716.         return;
  1717.  
  1718.     cmdline_height = Rows;
  1719.     for (wp = firstwin; wp; wp = wp->w_next)
  1720.     {
  1721.         cmdline_height -= wp->w_height + wp->w_status_height;
  1722.         if (wp->w_buffer == NULL)        /* just in case */
  1723.             continue;
  1724. #ifdef SCROLL_PAST_END
  1725.         max = wp->w_buffer->b_ml.ml_line_count;
  1726. #else
  1727.         max = wp->w_buffer->b_ml.ml_line_count + wp->w_height - 1;
  1728. #endif
  1729.         if (max < 1)                    /* empty buffer */
  1730.             max = 1;
  1731.         val = wp->w_topline;
  1732.         size = wp->w_height;
  1733. #ifdef SCROLL_PAST_END
  1734.         if (val > max)                    /* just in case */
  1735.             val = max;
  1736. #else
  1737.         if (size > max)                    /* just in case */
  1738.             size = max;
  1739.         if (val > max - size + 1)
  1740.         {
  1741.             val = max - size + 1;
  1742.             if (val < 1)                /* minimal value is 1 */
  1743.                 val = 1;
  1744.         }
  1745. #endif
  1746.         if (size < 1 || wp->w_botline - 1 > max)
  1747.         {
  1748.             /*
  1749.              * This can happen during changing files.  Just don't update the
  1750.              * scrollbar for now.
  1751.              */
  1752.         }
  1753.         else if (wp->w_scrollbar.height == 0)
  1754.         {
  1755.             /* Must be a new window */
  1756.             wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_CREATE;
  1757.             wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_CREATE;
  1758.             wp->w_scrollbar.value = val;
  1759.             wp->w_scrollbar.size = size;
  1760.             wp->w_scrollbar.max = max;
  1761.             wp->w_scrollbar.top = wp->w_winpos;
  1762.             wp->w_scrollbar.height = wp->w_height;
  1763.             wp->w_scrollbar.status_height = wp->w_status_height;
  1764.             gui.num_scrollbars++;
  1765.             worst_update = SB_UPDATE_CREATE;
  1766.         }
  1767.         else if (wp->w_scrollbar.top != wp->w_winpos
  1768.             || wp->w_scrollbar.height != wp->w_height
  1769.             || wp->w_scrollbar.status_height != wp->w_status_height)
  1770.         {
  1771.             /* Height of scrollbar has changed */
  1772.             wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_HEIGHT;
  1773.             wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
  1774.             wp->w_scrollbar.value = val;
  1775.             wp->w_scrollbar.size = size;
  1776.             wp->w_scrollbar.max = max;
  1777.             wp->w_scrollbar.top = wp->w_winpos;
  1778.             wp->w_scrollbar.height = wp->w_height;
  1779.             wp->w_scrollbar.status_height = wp->w_status_height;
  1780.             if (worst_update < SB_UPDATE_HEIGHT)
  1781.                 worst_update = SB_UPDATE_HEIGHT;
  1782.         }
  1783.         else if (wp->w_scrollbar.value != val
  1784.             || wp->w_scrollbar.size != size
  1785.             || wp->w_scrollbar.max != max)
  1786.         {
  1787.             /* Thumb of scrollbar has moved */
  1788.             wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_VALUE;
  1789.             wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_VALUE;
  1790.             wp->w_scrollbar.value = val;
  1791.             wp->w_scrollbar.size = size;
  1792.             wp->w_scrollbar.max = max;
  1793.             if (worst_update < SB_UPDATE_VALUE)
  1794.                 worst_update = SB_UPDATE_VALUE;
  1795.         }
  1796.  
  1797.         /*
  1798.          * We may have just created the left scrollbar say, when we already had
  1799.          * the right one.
  1800.          */
  1801.         if (gui.new_sb[SB_LEFT])
  1802.             wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_CREATE;
  1803.         if (gui.new_sb[SB_RIGHT])
  1804.             wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_CREATE;
  1805.     }
  1806.     if (cmdline_height < 1)
  1807.         cmdline_height = 1;             /* Shouldn't happen, but just in case */
  1808.  
  1809.     /* Check the command line scrollbar */
  1810.     if (gui.cmdline_sb.height != cmdline_height
  1811.         || gui.cmdline_sb.status_height != lastwin->w_status_height)
  1812.     {
  1813.         /* Height of scrollbar has changed */
  1814.         gui.cmdline_sb.update[SB_LEFT] = SB_UPDATE_HEIGHT;
  1815.         gui.cmdline_sb.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
  1816.         gui.cmdline_sb.value = 0;
  1817.         gui.cmdline_sb.size = 1;            /* No thumb */
  1818.         gui.cmdline_sb.max = 0;
  1819.         gui.cmdline_sb.top = Rows - cmdline_height;
  1820.         gui.cmdline_sb.height = cmdline_height;
  1821.         gui.cmdline_sb.status_height = lastwin->w_status_height;
  1822.         if (worst_update < SB_UPDATE_HEIGHT)
  1823.             worst_update = SB_UPDATE_HEIGHT;
  1824.     }
  1825.  
  1826.     /*
  1827.      * If we have just created the left or right scrollbar-box, then we need to
  1828.      * update the height of the command line scrollbar (it will already be
  1829.      * created).
  1830.      */
  1831.     if (gui.new_sb[SB_LEFT])
  1832.     {
  1833.         gui.cmdline_sb.update[SB_LEFT] = SB_UPDATE_HEIGHT;
  1834.         worst_update = SB_UPDATE_CREATE;
  1835.         gui.new_sb[SB_LEFT] = FALSE;
  1836.     }
  1837.     if (gui.new_sb[SB_RIGHT])
  1838.     {
  1839.         gui.cmdline_sb.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
  1840.         worst_update = SB_UPDATE_CREATE;
  1841.         gui.new_sb[SB_RIGHT] = FALSE;
  1842.     }
  1843.  
  1844.     if (worst_update != SB_UPDATE_NOTHING)
  1845.     {
  1846.         if (gui.which_scrollbars[SB_LEFT] && gui.dragged_sb != SB_LEFT)
  1847.             gui_mch_update_scrollbars(worst_update, SB_LEFT);
  1848.         if (gui.which_scrollbars[SB_RIGHT] && gui.dragged_sb != SB_RIGHT)
  1849.             gui_mch_update_scrollbars(worst_update, SB_RIGHT);
  1850.     }
  1851. }
  1852.  
  1853. /*
  1854.  * Scroll a window according to the values set in the globals current_scrollbar
  1855.  * and scrollbar_value.  Return TRUE if the cursor in the current window moved
  1856.  * or FALSE otherwise.
  1857.  */
  1858.     int
  1859. gui_do_scroll()
  1860. {
  1861.     WIN        *wp, *old_wp;
  1862.     int        i;
  1863.     FPOS    old_cursor;
  1864.  
  1865.     for (wp = firstwin, i = 0; i < current_scrollbar; i++)
  1866.     {
  1867.         if (wp == NULL)
  1868.             break;
  1869.         wp = wp->w_next;
  1870.     }
  1871.     if (wp != NULL)
  1872.     {
  1873.         old_cursor = curwin->w_cursor;
  1874.         old_wp = curwin;
  1875.         curwin = wp;
  1876.         curbuf = wp->w_buffer;
  1877.         i = (long)scrollbar_value - (long)wp->w_topline;
  1878.         if (i < 0)
  1879.             scrolldown(-i);
  1880.         else if (i > 0)
  1881.             scrollup(i);
  1882.         if (p_so)
  1883.             cursor_correct();
  1884.         coladvance(curwin->w_curswant);
  1885.  
  1886.         curwin = old_wp;
  1887.         curbuf = old_wp->w_buffer;
  1888.  
  1889.         if (wp == curwin)
  1890.             cursupdate();            /* fix window for 'so' */
  1891.         wp->w_redr_type = VALID;
  1892.         updateWindow(wp);        /* update window, status line, and cmdline */
  1893.  
  1894.         return !equal(curwin->w_cursor, old_cursor);
  1895.     }
  1896.     else
  1897.     {
  1898.         /* Command-line scrollbar, unimplemented */
  1899.         return FALSE;
  1900.     }
  1901. }
  1902.  
  1903.  
  1904. /*
  1905.  * Horizontal scrollbar stuff:
  1906.  */
  1907.  
  1908.     static void
  1909. gui_update_horiz_scrollbar()
  1910. {
  1911.     int        value, size, max;
  1912.  
  1913.     if (!gui.which_scrollbars[SB_BOTTOM])
  1914.         return;
  1915.  
  1916.     if (gui.dragged_sb == SB_BOTTOM)
  1917.         return;
  1918.  
  1919.     if (curwin->w_p_wrap && gui.prev_wrap)
  1920.         return;
  1921.             
  1922.     /*
  1923.      * It is possible for the cursor to be invalid if we're in the middle of
  1924.      * something (like changing files).  If so, don't do anything for now.
  1925.      */
  1926.     if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
  1927.         return;
  1928.  
  1929.     if (curwin->w_p_wrap)
  1930.     {
  1931.         value = 0;
  1932.         size = Columns;
  1933. #ifdef SCROLL_PAST_END
  1934.         max = 0;
  1935. #else
  1936.         max = Columns - 1;
  1937. #endif
  1938.     }
  1939.     else
  1940.     {
  1941.         value = curwin->w_leftcol;
  1942.         size = Columns;
  1943. #ifdef SCROLL_PAST_END
  1944.         max = gui_get_max_horiz_scroll();
  1945. #else
  1946.         max = gui_get_max_horiz_scroll() + Columns - 1;
  1947. #endif
  1948.     }
  1949.     gui_mch_update_horiz_scrollbar(value, size, max + 1);
  1950.     gui.prev_wrap = curwin->w_p_wrap;
  1951. }
  1952.  
  1953. /*
  1954.  * Determine the maximum value for scrolling right.
  1955.  */
  1956.     int
  1957. gui_get_max_horiz_scroll()
  1958. {
  1959.     int        max = 0;
  1960.     char_u    *p;
  1961.  
  1962.     p = ml_get_curline();
  1963.     if (p[0] != NUL)
  1964.         while (p[1] != NUL)                /* Don't count last character */
  1965.             max += chartabsize(*p++, (colnr_t)max);
  1966.     return max;
  1967. }
  1968.  
  1969. /*
  1970.  * Do a horizontal scroll.  Return TRUE if the cursor moved, or FALSE otherwise
  1971.  */
  1972.     int
  1973. gui_do_horiz_scroll()
  1974. {
  1975.     char_u    *p;
  1976.     int        i;
  1977.     int        vcol;
  1978.     int        ret_val = FALSE;
  1979.  
  1980.     /* no wrapping, no scrolling */
  1981.     if (curwin->w_p_wrap)
  1982.         return FALSE;
  1983.  
  1984.     curwin->w_leftcol = scrollbar_value;
  1985.  
  1986.     i = 0;
  1987.     vcol = 0;
  1988.     p = ml_get_curline();
  1989.     while (p[i] && i < curwin->w_cursor.col && vcol < curwin->w_leftcol)
  1990.         vcol += chartabsize(p[i++], (colnr_t)vcol);
  1991.     if (vcol < curwin->w_leftcol)
  1992.     {
  1993.         /*
  1994.          * Cursor is on a character that is at least partly off the left hand
  1995.          * side of the screen.
  1996.          */
  1997.         while (p[i] && vcol < curwin->w_leftcol)
  1998.             vcol += chartabsize(p[i++], (colnr_t)vcol);
  1999.         curwin->w_cursor.col = i;
  2000.         curwin->w_set_curswant = TRUE;
  2001.         ret_val = TRUE;
  2002.     }
  2003.  
  2004.     while (p[i] && i <= curwin->w_cursor.col
  2005.                                     && vcol <= curwin->w_leftcol + Columns)
  2006.         vcol += chartabsize(p[i++], (colnr_t)vcol);
  2007.     if (vcol > curwin->w_leftcol + Columns)
  2008.     {
  2009.         /*
  2010.          * Cursor is on a character that is at least partly off the right hand
  2011.          * side of the screen.
  2012.          */
  2013.         if (i < 2)
  2014.             i = 0;
  2015.         else
  2016.             i -= 2;
  2017.         curwin->w_cursor.col = i;
  2018.         curwin->w_set_curswant = TRUE;
  2019.         ret_val = TRUE;
  2020.     }
  2021.     updateScreen(NOT_VALID);
  2022.     return ret_val;
  2023. }
  2024.